package cn.stimd.spring.beans.factory.support;

import cn.stimd.spring.beans.BeansException;
import cn.stimd.spring.beans.factory.DisposableBean;
import cn.stimd.spring.beans.factory.ObjectFactory;
import cn.stimd.spring.beans.factory.config.SingletonBeanRegistry;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Created by Stimd
 * 项目地址：https://gitee.com/stimd/spring-wheel
 * 公众号：Java编程探微
 */
public class DefaultSingletonBeanRegistry implements SingletonBeanRegistry {
    protected final Log logger = LogFactory.getLog(getClass());
    protected static final Object NULL_OBJECT = new Object();

    //一级缓存，创建完成的单例Bean缓存
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
    //二级缓存，缓存提前暴露的单例（Bean尚在创建中），用于解决普通对象的循环依赖
    private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
    //三级缓存，单例工厂的缓存，解决代理对象的循环依赖
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

    //单例Bean名称缓存，还记录了Bean的加入顺序
    private final Set<String> registeredSingletons = new LinkedHashSet<>(256);
    //创建中的Bean的集合，所有的单例在创建开始前被标记，于创建结束后取消标记
    private final Set<String> singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap<>(16));
    //待销毁的Bean集合
    private final Map<String, Object> disposableBeans = new LinkedHashMap<>();


    @Override
    public void registerSingleton(String beanName, Object singletonObject) {
        synchronized (this.singletonObjects) {
            Object obj = this.singletonObjects.get(beanName);
            if (obj != null) {
                throw new IllegalStateException("注册单例Bean失败，[" + beanName + "]已存在");
            }

            addSingleton(beanName, singletonObject);
        }
    }


    @Override
    public Object getSingleton(String beanName) {
        return getSingleton(beanName, true);
    }

    protected Object getSingleton(String beanName, boolean allowEarlyReference) {
        //1. 一级缓存，已经创建完毕的Bean
        Object singletonObject = this.singletonObjects.get(beanName);

        if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {

            //1) 单例Bean此时不存在，尚未创建完成
            //2) 同时被标记为“创建中”，说明getSingleton(String, ObjectFactory)方法被调用过
            //也就是说A正在创建，再一次调用创建A的方法，说明是B循环依赖了A
            //这里我们将未创建完成的实例直接返回，AbstractBeanFactory不会继续调用createBean方法，循环依赖问题也就被解决了
            synchronized (this.singletonObjects) {
                //2. 二级缓存，说明Bean尚在创建中（如果存在，说明三级缓存已经调用过了）
                singletonObject = this.earlySingletonObjects.get(beanName);

                if (singletonObject == null && allowEarlyReference) {
                    //3. 三级缓存，主要用于解决代理对象的循环依赖（二级缓存保证了三级缓存只会调用一次）
                    ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                    if (singletonFactory != null) {
                        //这里的ObjectFactory是AbstractAutowireCapableBeanFactory提供的
                        //目的是为了解决代理对象问题，对于普通对象没有影响
                        singletonObject = singletonFactory.getObject();

                        //二、三级缓存互斥，添加到二级缓存的同时，从三级缓存中移除
                        this.earlySingletonObjects.put(beanName, singletonObject);
                        this.singletonFactories.remove(beanName);
                    }
                }
            }
        }
        return (singletonObject != NULL_OBJECT ? singletonObject : null);
    }


    //尝试从缓存中获取实例，如不存在则创建实例
    public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
        synchronized (singletonObjects){
            Object singletonObject = this.singletonObjects.get(beanName);
            if(singletonObject == null){
                //1) 标记为创建中的Bean
                beforeSingletonCreation(beanName);

                boolean newSingleton = false;
                try {
                    //2) 回调执行创建对象的流程
                    singletonObject = singletonFactory.getObject();
                    newSingleton = true;
                }finally {
                    //3) 取消创建中的标记
                    afterSingletonCreation(beanName);
                }

                //4) 将对象添加到一级缓存，同时从二级缓存中移除
                //此时的Bean是完全创建的，即使有循环依赖，也得到了解决
                if (newSingleton) {
                    addSingleton(beanName, singletonObject);
                }
            }
            return (singletonObject != NULL_OBJECT ? singletonObject : null);
        }
    }


    @Override
    public boolean containsSingleton(String beanName) {
        return this.registeredSingletons.contains(beanName);
    }


    /**
     * 添加指定beanName单例，此时的Bean已创建完成（即添加到一级缓存）
     * 注：一级缓存与二、三级缓存是互斥的
     */
    protected void addSingleton(String beanName, Object singletonObject) {
        synchronized (this.singletonObjects) {
            //添加到一级缓存
            this.singletonObjects.put(beanName, (singletonObject != null ? singletonObject : NULL_OBJECT));
            this.singletonFactories.remove(beanName);       //从三级缓存中移除
            this.earlySingletonObjects.remove(beanName);    //从二级缓存中移除
            this.registeredSingletons.add(beanName);
        }
    }


    /**
     * 添加指定beanName的单例工厂，此时的Bean尚在创建中（即添加到三级缓存）
     * 注：二、三级缓存也是互斥的，在getSingleton(String, boolean)方法中，从三级缓存中取出Bean并放到二级缓存中
     */
    protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
        synchronized (this.singletonObjects) {
            if (!this.singletonObjects.containsKey(beanName)) {
                this.singletonFactories.put(beanName, singletonFactory);    //添加到三级缓存
                this.earlySingletonObjects.remove(beanName);                //从二级缓存中移除
                this.registeredSingletons.add(beanName);
            }
        }
    }


    protected void beforeSingletonCreation(String beanName) {
        if (!this.singletonsCurrentlyInCreation.add(beanName)) {
            throw new BeansException("依赖的Bean[" + beanName + "]正在创建中，是否存在未解决的循环依赖？");
        }
    }


    protected void afterSingletonCreation(String beanName) {
        if (!this.singletonsCurrentlyInCreation.remove(beanName)) {
            throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation");
        }
    }


    public boolean isSingletonCurrentlyInCreation(String beanName) {
        return this.singletonsCurrentlyInCreation.contains(beanName);
    }


    public boolean isCurrentlyInCreation(String beanName) {
        return isSingletonCurrentlyInCreation(beanName);
    }


    public final Object getSingletonMutex() {
        return this.singletonObjects;
    }

    //----------------------------------------销毁相关

    //注册需要销毁的Bean
    public void registerDisposableBean(String beanName, DisposableBean bean) {
        synchronized (this.disposableBeans) {
            this.disposableBeans.put(beanName, bean);
        }
    }


    public void destroySingletons() {
        String[] disposableBeanNames = StringUtils.toStringArray(this.disposableBeans.keySet());
        for (int i = disposableBeanNames.length - 1; i >= 0; i--) {
            destroySingleton(disposableBeanNames[i]);
        }
    }

    public void destroySingleton(String beanName) {
        //从缓存中移除单例
        removeSingleton(beanName);

        //获取适配器对象，同时从待销毁的集合中
        DisposableBean disposableBean;
        synchronized (this.disposableBeans) {
            disposableBean = (DisposableBean) this.disposableBeans.remove(beanName);
        }
        //销毁单例
        destroyBean(beanName, disposableBean);
    }

    protected void removeSingleton(String beanName) {
        synchronized (this.singletonObjects) {
            this.singletonObjects.remove(beanName);
            this.singletonFactories.remove(beanName);
            this.earlySingletonObjects.remove(beanName);
            this.registeredSingletons.remove(beanName);
        }
    }

    protected void destroyBean(String beanName, DisposableBean bean) {
        if(bean != null){
            try {
                //此处的bean实际是单例对应的DisposableBeanAdapter，由适配器来完成具体的销毁逻辑
                bean.destroy();
            } catch (Exception e) {
                logger.error("[Beans] [生命周期] --> 执行单例[" + beanName + "]的销毁方法时出现异常", e);
            }
        }
    }
}
